/* Copyright (C) 2010  Romolo Camplani, Marco Fortunato, Marco Marelli, Guido Rota, Fabio A. Schreiber et al.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package org.dei.perla.sys.hlqexecutor.datastructures;

import java.util.ArrayList;


/**
 * Implementa un oggetto Nodo utile per organizzare i dati in una struttara ad albero n-ario. 
 * ATTENZIONE: L'oggetto memorizzato nel nodo non viene restituito come copia e neppure il 
 * il cotruttore fa una copia dei parametri. Alcuni metodi sono protected proprio perchè non 
 * si vuole che vengano utilizzati al di fuori di questo package.
 * @author  Luigi Rinaldi
 */

public class Node<T> {
 
	private T obj;
	private Node<T> parent;
	private ArrayList<Node<T>> children;
	
	
	/**
	 * Costruttore
	 * Crea un nodo e inserisce all'interno di esso l'informazione.
	 * @param obj un oggetto generic G.
	 */
	public Node(T obj) {
		this.setNode(obj);
		this.setParent(null);
		this.children = new ArrayList<Node<T>>();
	}
	
	protected Node(T obj, Node<T> parent, Node<T>... node) {
		this.setNode(obj);
		this.setParent(parent);
		this.children = new ArrayList<Node<T>>();
		for(Node<T> child : node) 
			this.addChild(child);
	}
	
	protected Node(T obj, Node<T> parent, ArrayList<Node<T>> node) {
		this.setNode(obj);
		this.setParent(parent);
		this.children = new ArrayList<Node<T>>();
		for(Node<T> child : node) 
			this.addChild(child);
	}
	
	/**
	 * Permette di impostare i figli un nodo.
	 * @param child un oggetto Node.
	 */
	protected boolean addChild(Node<T> child) {
		if (child != null)
			return this.children.add(child);
		return false;
	}
	
	/**
	 * Rimuove il figlio child dal nodo.
	 * @param child un oggetto Node.
	 */
	protected boolean removeChild(Node<T> child) {
		if (child != null)
			return this.children.remove(child);
		return false;
	}
	
	/**
	 * Restituisce i nodi figli del nodo.
	 * @return ArrayList di oggetti Node.
	 */
	public ArrayList<Node<T>> getChildren() {
		return this.children;
	}
	
	/**
	 * Permette di settare il contenuto informativo del nodo. 
	 * @param obj un oggetto generic G
	 */
	protected void setNode(T obj) {
		this.obj = obj;
	}

	/**
	 * Restituisce il contenuto informativo di un nodo.
	 * @return un oggetto generic T.
	 */
	public T getObject() {
		return this.obj;
	}
	
	/**
	 * Permette eventualmente di impostare un nodo padre.
	 * @param parent un oggetto generic T.
	 */
	protected void setParent(Node<T> parent) {
		this.parent = parent;
	}

	/**
	 * Permette di conoscere qual è il padre del nodo.
	 * @return un oggetto Node.
	 */
	public Node<T> getParent() {
		return this.parent;
	}
	
	/**
	 * Fornisce informazioni sul livello del nodo. Restituisce 0 se si tratta di un nodo radice.
	 * Ovviamente è una funzione che può essere usata solo se il nodo è parte di un albero.
	 * In presenza di nodi di altri tipi di strutture dati non fornisce informazioni valide. 
	 * @return un int.
	 */
	public int getLevel() {
		if(this.parent == null)
			return 0;
		else 
			return 1 + this.parent.getLevel();
	}
	
	/**
	 * Permette di sapere se il nodo è root
	 * @return un boolean
	 */
    public boolean isRoot() {
		return parent == null;
	}
	
    /**
     * Permette di sapere se il nodo è foglia
     * @return un boolean.
     */
	public boolean isLeaf() {
		return children.isEmpty();
	}
	
	/**
	 * Restituisce una copia dell'oggetto Nodo. Ovviamente non vengono clonati il padre e figli 
	 * nel caso in cui siano presenti, ma il nuovo nodo avrà comunque i riferimenti a quest'ultimi.
	 * anche tutta la struttura del nodo.
	 */
	public Node<T> clone() {
		Node<T> tmp = new Node<T>(this.obj,this.parent);
		for(Node<T> child : this.children)
			tmp.addChild(child);
		return tmp;
	}
	
	/**
	 * Permette di sapere se due nodi sono uguali.
	 * Due nodi si intendono uguali se l'oggetto contenuto al loro interno è uguale.	
	 * @param node
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean equals(Object otherObject) {
		
		if (this == otherObject)
			return true;
		
		if (this == null)
			return false;
		
		if (getClass() != otherObject.getClass())
			return false;
		
		Node<T> other = (Node<T>) otherObject; 
		
		if(!this.obj.equals(other.getObject()))
			return false;
		
		return true;
	}
}